package com.maozzi.common.util;

import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.maozzi.common.enums.BizCodeEnum;
import com.maozzi.common.exception.BizException;
import com.maozzi.common.model.PageDTO;
import com.maozzi.common.model.PageVO;
import org.springframework.cglib.beans.BeanCopier;
import org.springframework.util.ObjectUtils;

import java.util.ArrayList;
import java.util.List;

/**
 * 复制工具类
 *
 * @author maozi
 */
public class CloneUtil {

    /**
     * 单个对象之间的属性复制，适用于VO、DTO、DO之间的相互转换
     *
     * @param source 源对象
     * @param target 目标对象
     * @param <T>    源对象 泛型
     * @param <E>    目标对象 泛型
     * @return 返回目标对象
     */
    public static <T, E> E to(T source, E target) {
        if (ObjectUtils.isEmpty(source) || ObjectUtils.isEmpty(target)) {
            throw new BizException(BizCodeEnum.CAST_ERROR, "空数据类型");
        }

        BeanCopier copier = BeanCopier.create(source.getClass(), target.getClass(), false);
        copier.copy(source, target, null);

        return target;
    }

    /**
     * 单个对象之间的属性复制，适用于VO、DTO、DO之间的相互转换
     *
     * @param source 源对象
     * @param clz    目标Class类型
     * @param <T>    源对象 泛型
     * @param <E>    目标对象 泛型
     * @return 返回目标对象
     */
    public static <T, E> E to(T source, Class<E> clz) {
        return CloneUtil.toInner(source, clz);
    }

    /**
     * 集合中对象之间属性复制
     *
     * @param source 源对象
     * @param clz    目标Class类型
     * @param <T>    源对象 泛型
     * @param <E>    目标对象 泛型
     * @return 返回目标对象
     */
    public static <T, E> List<E> toList(List<T> source, Class<E> clz) {
        List<E> list = new ArrayList<>();
        if (ObjectUtils.isEmpty(source)) {
            return list;
        }

        BeanCopier copier = null;
        for (T t : source) {
            if (ObjectUtils.isEmpty(copier)) {
                copier = BeanCopier.create(t.getClass(), clz, false);
            }

            list.add(toInner(t, clz, copier));
        }

        return list;
    }

    /**
     * 分页结果复制为分页DTO对象
     *
     * @param page 分页 源对象
     * @param clz  目标Class类型
     * @param <T>  源对象 泛型
     * @param <E>  目标对象 泛型
     * @return 返回目标对象
     */
    public static <T, E> PageDTO<E> toPageDTO(Page<T> page, Class<E> clz) {
        if (ObjectUtils.isEmpty(page)) {
            throw new BizException(BizCodeEnum.WRONG_PARAMETERS, "空数据类型");
        }

        PageDTO<E> dto = new PageDTO<>(page.getCurrent(), page.getSize());

        dto.setPageNum(page.getPages());
        dto.setTotal(page.getTotal());

        List<E> items = CloneUtil.toList(page.getRecords(), clz);
        dto.setItems(items);

        return dto;
    }

    /**
     * 分页DTO结果复制为分页VO对象
     *
     * @param pageDTO 分页 源对象
     * @param clz     目标Class类型
     * @param <T>     源对象 泛型
     * @param <E>     目标对象 泛型
     * @return 返回目标对象
     */
    public static <T, E> PageVO<E> toPageVO(PageDTO<T> pageDTO, Class<E> clz) {
        PageVO<E> vo = new PageVO<>(pageDTO.getPage(), pageDTO.getPageSize());

        vo.setPageNum(pageDTO.getPageNum());
        vo.setTotal(pageDTO.getTotal());

        BeanCopier copier = BeanCopier.create(pageDTO.getItems().getClass(), clz, false);
        List<E> list = CloneUtil.toList(pageDTO.getItems(), clz);
        vo.setItems(list);

        return vo;
    }

    /**
     * 单个bean之间的复制（内部）
     *
     * @param source 源对象
     * @param clz    目标Class类型
     * @param <T>    源对象 泛型
     * @param <E>    目标对象 泛型
     * @return 返回目标对象
     */
    private static <T, E> E toInner(T source, Class<E> clz) {
        if (ObjectUtils.isEmpty(source)) {
            throw new BizException(BizCodeEnum.WRONG_PARAMETERS, "空数据类型");
        }

        BeanCopier copier = BeanCopier.create(source.getClass(), clz, false);

        return CloneUtil.toInner(source, clz, copier);
    }

    /**
     * 单个bean之间的复制（内部）
     *
     * @param source 源对象
     * @param clz    目标Class类型
     * @param copier cglib copier实例
     * @param <T>    源对象 泛型
     * @param <E>    目标对象 泛型
     * @return 返回目标对象
     */
    private static <T, E> E toInner(T source, Class<E> clz, BeanCopier copier) {
        if (ObjectUtils.isEmpty(source)) {
            throw new BizException(BizCodeEnum.WRONG_PARAMETERS, "空数据类型");
        }

        E target;
        try {
            target = clz.getDeclaredConstructor().newInstance();
            copier.copy(source, target, null);
        } catch (Exception e) {
            throw new BizException(BizCodeEnum.CAST_ERROR);
        }

        return target;
    }
}
